package xtime

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

const (
	DateFormat          = "2006-01-02"
	DateTimeFormat      = "2006-01-02 15:04:05"
	MicroDateTimeFormat = "2006-01-02 15:04:05.000000"
	DateStringFormat    = "20060102"
	MonthStringFormat   = "200601"
)

// 格式化20060102日期字符串
func FormatNolineStringToTime(datetime string) time.Time {
	local, _ := time.LoadLocation("Local")
	parseStrTime, _ := time.ParseInLocation(DateStringFormat, datetime, local)
	return parseStrTime
}

// 格式化日期字符串
func FormatStringToTime(datetime string) time.Time {
	local, _ := time.LoadLocation("Local")
	parseStrTime, _ := time.ParseInLocation(DateTimeFormat, datetime, local)
	return parseStrTime
}

// 格式化日期到秒
func TimeToUnix(date string) int64 {
	loc, _ := time.LoadLocation("Local")
	timeUnix, _ := time.ParseInLocation(DateTimeFormat, date, loc)
	return timeUnix.Unix()
}

// 格式化小时分钟到当天时间
func HourTimeToUnix(date string) (r time.Time, err error) {
	loc, _ := time.LoadLocation("Local")
	now := time.Now()
	var tTime time.Time
	tTime, err = time.ParseInLocation("15:04", date, loc)
	if err != nil {
		return
	}
	r = time.Date(now.Year(), now.Month(), now.Day(), tTime.Hour(), tTime.Minute(), tTime.Second(), tTime.Nanosecond(), loc)
	return
}

// 格式化Json标准时间
func FormatJsonTime(datetime string) string {
	local, _ := time.LoadLocation("Local")
	t, _ := time.ParseInLocation("2006-01-02T15:04:05Z07:00", datetime, local)
	return time.Time(t).Format(DateTimeFormat)
}

// 格式化日期
func UnixToTime(t int64, layout string) string {
	if t == 0 {
		t = time.Now().Unix()
	}
	if layout == "" {
		layout = DateTimeFormat
	}
	return time.Unix(t, 0).Format(layout)
}

func NowDateToUnix() int64 {
	t, _ := time.Parse("2006-01-02", time.Now().Format("2006-01-02"))
	return t.Unix()
}

func NowTime() string {
	return time.Now().Format(DateTimeFormat)
}

func NowDate() string {
	return time.Now().Format("20060102")
}

// 返回时间的前一天
func PreDay(t time.Time) time.Time {
	if t.Unix() == 0 {
		t = time.Now()
	}
	return time.Date(t.Year(), t.Month(), t.Day()-1, 0, 0, 0, 0, time.Local)
}

// 返回给定时间的该月第一天
func MonthFirstDay(t time.Time) time.Time {
	return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.Local)
}

func PrevMonth(t time.Time, deltaMonth int) (lastMonth string) {
	var lastMonthTime time.Time
	deltaDay := 0
	for {
		lastMonthTime = t.AddDate(0, -deltaMonth, deltaDay)
		if lastMonthTime.Month() == t.Month() {
			deltaDay--
			continue
		}
		break
	}
	lastMonth = lastMonthTime.Format("200601")
	return
}

func TraceRun(t time.Time) {
	millisecond := time.Since(t).Nanoseconds() / (1000 * 1000)
	fmt.Println(fmt.Sprintf("%dms", millisecond))
}

// 返回时间戳对应的天数
func NowAppointDays(timestamp int64) int64 {
	_, offset := time.Now().Zone()
	return (timestamp + int64(offset)) / (3600 * 24)
}

// Time be used to MySql timestamp converting.
type Time int64

// Scan scan time.
func (jt *Time) Scan(src interface{}) (err error) {
	switch sc := src.(type) {
	case time.Time:
		*jt = Time(sc.Unix())
	case string:
		var i int64
		i, err = strconv.ParseInt(sc, 10, 64)
		*jt = Time(i)
	}
	return
}

// Value get time value.
func (jt Time) Value() (driver.Value, error) {
	return time.Unix(int64(jt), 0), nil
}

// Time get time.
func (jt Time) Time() time.Time {
	return time.Unix(int64(jt), 0)
}

type Duration time.Duration

// UnmarshalText unmarshal text to duration.
func (d *Duration) UnmarshalText(text []byte) error {
	tmp, err := time.ParseDuration(string(text))
	if err == nil {
		*d = Duration(tmp)
	}
	return err
}

func TimeBefore(time1 string, time2 string) bool {
	t1, err := time.Parse(DateTimeFormat, time1)
	if err != nil {
		return false
	}
	t2, err := time.Parse(DateTimeFormat, time2)
	if err != nil {
		return false
	}
	if t1.Before(t2) {
		return true
	}
	return false
}

// NowDateStartToUnix returns the start unix of now date
func NowDateStartToUnix() int64 {
	now := time.Now()
	return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
}

// 返回给定时间的当天零点开始时间戳
func DateStartToUnix(datetime string, layout string) int64 {
	if layout == "" {
		layout = DateTimeFormat
	}
	loc, _ := time.LoadLocation("Local")
	timeL, _ := time.ParseInLocation(layout, datetime, loc)
	return time.Date(timeL.Year(), timeL.Month(), timeL.Day(), 0, 0, 0, 0, timeL.Location()).Unix()
}

// NowMonth returns the moth of year
func NowMonth() string {
	return time.Now().Format(MonthStringFormat)
}

// PreMonth returns the moth of year
func PreMonth() string {
	now := time.Now()
	preMonth := time.Date(now.Year(), now.Month()-1, now.Day(), 0, 0, 0, 0, now.Location())
	return preMonth.Format(MonthStringFormat)
}

type JSONDayTime time.Time

func (jt *JSONDayTime) MarshalJSON() ([]byte, error) {
	stamp := fmt.Sprintf("\"%s\"", time.Time(*jt).Format("2006-01-02"))
	return []byte(stamp), nil
}

func (jt *JSONDayTime) UnmarshalJSON(bs []byte) error {
	var s string
	err := json.Unmarshal(bs, &s)
	if err != nil {
		return err
	}
	t, err := time.ParseInLocation("2006-01-02", s, time.Local)
	if err != nil {
		return err
	}
	*jt = JSONDayTime(t)
	return nil
}

type JSONDateTime time.Time

func (jt *JSONDateTime) MarshalJSON() ([]byte, error) {
	stamp := fmt.Sprintf("\"%s\"", time.Time(*jt).Format(DateTimeFormat))
	return []byte(stamp), nil
}

func (jt *JSONDateTime) UnmarshalJSON(bs []byte) error {
	var s string
	err := json.Unmarshal(bs, &s)
	if err != nil {
		return err
	}
	t, err := time.ParseInLocation(DateTimeFormat, s, time.Local)
	if err != nil {
		return err
	}
	*jt = JSONDateTime(t)
	return nil
}

// UnixToDate returns the beginning of the day
func UnixToDateBeginning(unixSec int64) (t time.Time) {
	unix := time.Unix(unixSec, 0)
	t = time.Date(unix.Year(), unix.Month(), unix.Day(), 0, 0, 0, 0, unix.Location())
	return
}

func MicroDateTimeFormatNow() string {
	return time.Now().Format(MicroDateTimeFormat)
}
